package stock.fish.util;

import stock.fish.Box;
import stock.fish.Fish;
import stock.fish.FishConfig;

import java.util.*;
import java.util.stream.Collectors;

/*
* 排序优化：在组合选择前，对缓冲区内的鱼按重量降序排列，有助于更快地达到目标重量。
优先选择重鱼：增加找到合适组合的概率，减少未满足条件的情况。
* */
public class ImprovedFishBoxing {

    /*固定配置*/
    private static final int MAX_WEIGHT = FishConfig.MAX_WEIGHT;//箱子最大重量
    private static final int MIN_WEIGHT = FishConfig.MIN_WEIGHT;//箱子最小重量
    private static final int MIN_FISH_COUNT = FishConfig.MIN_FISH_COUNT;//装箱最小数量
    private static final int MAX_FISH_COUNT = FishConfig.MAX_FISH_COUNT;//装箱最大数量
    private static final int FISH_MIN_WEIGHT = 446;//最小鱼重量
    private static final int FISH_MAX_WEIGHT = 565;//最大鱼重量

    private static final int BUFFER_SIZE = 10;//缓冲计算数量 -- 为了增加匹配成功的概率


    /*计算参数*/
    private static  int maxSize = 0;//缓冲最大数量
    private static final  int abandonSize = 1;//每次处理进入废弃队列数
    private static final List<Fish> buffer = new ArrayList<>();//缓冲池
    private static final Box CURRENT_BOX = new Box();//当前操作箱子

    /*样本配置*/
    private static final int MIN_SAMPLE_SIZE = 500;//样本池
    private static final int MAX_SAMPLE_SIZE = 1000;//缓冲池最大
    private static final int LIMIT_SIZE = 30;//缓冲池最大限制

    /*装箱结果*/
    private static final List<Box> boxList = new ArrayList<>();
    private static final List<Fish> abandonList = new ArrayList<>();//废弃队列

    public static void main(String[] args) {
        Random rand = new Random();
        // 随机生成样本
        int fishCount = MIN_SAMPLE_SIZE + rand.nextInt(MAX_SAMPLE_SIZE - MIN_SAMPLE_SIZE);
        simulateFishFlow(fishCount);
        System.out.println("===============================剩余鱼数量："+buffer.size() + "===============================");
        System.out.println("剩余数据：" + Box.getPrintStr(buffer));
        System.out.println("剩余数据：" + buffer.stream().map(Fish::getWeight).sorted().collect(Collectors.toList()));
        System.out.println("===============================废弃队列数量："+abandonList.size() + "===============================");
        System.out.println("废弃队列：" + abandonList.stream().map(Fish::getWeight).sorted().collect(Collectors.toList()));
        System.out.println("===============================END===============================");
        System.out.println("===============================装箱完成数量："+ boxList.size() + "===============================");
        boxList.forEach(info -> System.out.println("鱼组合：" + Box.getPrintStr(info.getFishList()) +"，总条数: " + info.getFishCount() + ",总重量："+info.getWeight()));
        System.out.println("===============================END===============================");
        System.out.println("===============================打印结果===============================");
        System.out.println("样本数量："+fishCount);
        System.out.println("最大缓存池数量："+maxSize);
        System.out.println("装箱完成数量："+ boxList.size());
        System.out.println("===============================END===============================");

    }

    /**
     * 生成样本进行装箱
     */
    private static void simulateFishFlow(int fishCount) {
        Random rand = new Random();
        // 随机生成样本
        for (int i = 0; i < fishCount; i++) {
            int fishWeight = FISH_MIN_WEIGHT + rand.nextInt(FISH_MAX_WEIGHT - FISH_MIN_WEIGHT);
            buffer.add(new Fish(i+1,fishWeight));
            //如果缓冲池达到一定大小开始进行计算
            if (buffer.size() >= BUFFER_SIZE) {
                processBuffer();
            }
            if ( buffer.size() >= LIMIT_SIZE){
                System.out.println("进入废弃队列");
                //取出十条进入废弃队列
                List<Fish> fish = buffer.stream().sorted(Comparator.comparing(Fish::getWeight).reversed()).collect(Collectors.toList()).subList(0,abandonSize);
                abandonList.addAll(fish);
                buffer.removeAll(fish);
            }
        }
        //最后一次进行计算
        if (!buffer.isEmpty() && buffer.size() >= MIN_FISH_COUNT) {
            System.out.println("执行最后一次");
            processBuffer();
        }
    }

    /**
     * 在缓冲池中进行挑选
     */
    private static void processBuffer() {
        //记录缓存区最大值
        if (maxSize < buffer.size()){
            maxSize = buffer.size();
        }

        int minCount = MIN_FISH_COUNT - CURRENT_BOX.getFishCount();
        int maxCount = MAX_FISH_COUNT - CURRENT_BOX.getFishCount();
        int minWeight = MIN_WEIGHT - CURRENT_BOX.getWeight();
        int maxWeight = MAX_WEIGHT - CURRENT_BOX.getWeight();

        //进行从大到小排序 优先处理较重的鱼
        List<Fish> currentFish = buffer.stream().sorted(Comparator.comparing(Fish::getWeight).reversed()).collect(Collectors.toList());
        List<Fish> selectedFish = findBestCombination3(currentFish,minCount,maxCount,minWeight,maxWeight);
        //选出的鱼
        if (selectedFish != null && !selectedFish.isEmpty()) {
            buffer.removeAll(selectedFish);
            //进行装箱
            List<Fish> fish = new ArrayList<>(CURRENT_BOX.getFishList());
            fish.addAll(selectedFish);

            int totalWeight = Box.getWeightCalculate(fish);

            CURRENT_BOX.setFishList(fish);
            CURRENT_BOX.setCurrentWeight(totalWeight);
            //满足条件进行装箱
            if (CURRENT_BOX.isValid()){
                //满足条件进行装箱
                System.out.println("选出的鱼组合: " + Box.getPrintStr(fish) + "，总条数: " + fish.size() + "，总重量: " + totalWeight + "g");
                //深拷贝对象
                boxList.add(new Box(CURRENT_BOX.getFishList(),CURRENT_BOX.getWeight()));
                CURRENT_BOX.clear();
            }

        } else {
            System.out.println("未找到满足条件的组合，继续缓存");
        }

    }

    /**
     * 使用动态规划来实现
     * @param fishList 缓冲池
     * @return 返回选择数组
     */
    public static List<Fish> findBestCombination3(List<Fish> fishList,int minCount,int maxCount,int minWeight,int maxWeight) {

        // 寻找符合条件的最佳组合
        List<Fish> bestCombination = new ArrayList<>();
        //如果箱子可以放下这些鱼则尝试直接放入
        if (CURRENT_BOX.getFishCount() + fishList.size() <= MAX_FISH_COUNT){
            System.out.println("尝试直接放入");
            bestCombination.addAll(fishList);
            List<Fish> fish = new ArrayList<>(CURRENT_BOX.getFishList());
            fish.addAll(bestCombination);
            int weight = Box.getWeightCalculate(fish);
            //判断是否符合要求
            if (fish.size() > MAX_FISH_COUNT || weight > MAX_WEIGHT) {
                bestCombination  = new ArrayList<>();
                System.out.println("不符合要求");
            }else if (fish.size() >= MIN_FISH_COUNT && weight < MIN_WEIGHT) {
                bestCombination  = new ArrayList<>();
                System.out.println("不符合要求");
            }
        }
        if (bestCombination.isEmpty()){
            int n = fishList.size();
            // dp[i][j]表示选择i个元素，总重量为j时的最大权重
            int[][] dp = new int[maxCount + 1][maxWeight + 1];
            // 用于记录选择路径
            boolean[][][] path = new boolean[maxCount + 1][maxWeight + 1][n];
            // 初始化动态规划表
            for (int i = 0; i <= maxCount; i++) {
                for (int j = 0; j <= maxWeight; j++) {
                    dp[i][j] = -1; // 表示当前重量和选择个数的组合不可达
                }
            }
            dp[0][0] = 0; // 没有选任何鱼时，重量为0，且已选鱼数量为0
            // 动态规划计算
            for (int i = 0; i < n; i++) {
                int fishWeight = fishList.get(i).getWeight();
                // 遍历鱼的数量，从大到小更新dp数组
                for (int fishCount = maxCount; fishCount >= 1; fishCount--) {
                    // 遍历总重量从大到小更新，防止重复选择同一鱼
                    for (int weight = maxWeight; weight >= fishWeight; weight--) {
                        if (dp[fishCount - 1][weight - fishWeight] != -1) {
                            int newWeight = dp[fishCount - 1][weight - fishWeight] + fishWeight;
                            if (newWeight > dp[fishCount][weight]) {
                                dp[fishCount][weight] = newWeight;
                                // 记录路径
                                System.arraycopy(path[fishCount - 1][weight - fishWeight], 0, path[fishCount][weight], 0, n);
                                path[fishCount][weight][i] = true;
                            }
                        }
                    }
                }
            }

            for (int fishCount = minCount; fishCount <= maxCount; fishCount++) {
                for (int weight = minWeight; weight <= maxWeight; weight++) {
                    if (dp[fishCount][weight] >= minWeight && dp[fishCount][weight] <= maxWeight) {

                        List<Fish> combination = new ArrayList<>();
                        for (int i = 0; i < n; i++) {
                            if (path[fishCount][weight][i]) {
                                combination.add(fishList.get(i));
                            }
                        }
                        bestCombination = combination;
                        break;
                    }
                }
            }

        }
        return bestCombination;
    }
}
